#include "cv.h"
#include "highgui.h"

#include <cstdio>
#include <string>
#include <iostream>

#include <process.h>    /* _beginthread, _endthread */
#include "windows.h"      /* Mutex */

class FaceTracking {

   
	  friend unsigned __stdcall processImages( void *);

	  // Object attributes
	  void (*process)(IplImage *);
	  DWORD delay;
	  CvCapture* capture;
	  bool display;
	  bool call;
	  bool stop;
	  long fnumber;
	  CvFont font;
	
	  IplImage *img;
	  HANDLE hThread;
	  unsigned threadID;
	  HANDLE mut;

     void incFrameNumber() { WaitForSingleObject(mut, INFINITE); fnumber++; ReleaseMutex(mut); }

public:
     FaceTracking() : display(false), delay(0), capture(0), fnumber(0), img(0),
	       call(true), stop(true), hThread(0) {
               mut= CreateMutex(NULL, FALSE, NULL);
		         cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1);
     }

    ~FaceTracking() {
         stopIt(); // you should have stopped the capture before
		   if (hThread != 0)
			     CloseHandle( hThread );
		  CloseHandle(mut);
  		  cvDestroyWindow("Image (AviProcessor)");
		  cvReleaseCapture(&capture);
	  }

    
     static unsigned int WINAPI FaceTracking::processImages( void *g) {
          FaceTracking *proc= reinterpret_cast<FaceTracking *>(g);
          proc->setDisplay( true );
    //      while (!proc->isStopped()) {
             
             IplImage* frame, frameCopy, image;
              CvCapture* capture = 0;
             capture = cvCaptureFromCAM(0);

			      
		         int       key = 0;
               if( proc->capture ) {
                    while( key != 'q' ) {
                    cvGrabFrame(capture);          // capture a frame
                    
				        
                    /* get a frame */
                    frame = cvQueryFrame( capture );
	   		
                    /* always check */
				        if( !frame ) 
					          break;

				        /* 'fix' frame */
				        cvFlip( frame, frame, -1 );
				        frame->origin = 0;
                     cvShowImage( "Image (SpaceDodgeball)", frame );
				  HWND hWnd= (HWND)cvGetWindowHandle("Image (AviProcessor)");
				  ::SendMessage(hWnd,WM_PAINT,NULL,NULL); // force the window to repaint
               }
               
          

			// calling the process function
		//	if (proc->callingProcess())
	//			proc->process(proc->img);
	//		else
	//			Sleep(proc->getDelay());

			// displays image and frame number
		/*	if (proc->isDisplayed()) {

			     CvScalar sc;
				  char text[50];
				  sprintf(text,"Frame #%6d",proc->getFrameNumber());

				  sc= cvGet2D(proc->img,20,20);
				  if (sc.val[1]>128) 
				       cvPutText(proc->img, text, cvPoint(20,20), &(proc->font), cvScalar(0,0,0));
				  else
					    cvPutText(proc->img, text, cvPoint(20,20), &(proc->font), cvScalar(255,255,255));

				  cvShowImage( "Image (SpaceDodgeball)", proc->img );
				  HWND hWnd= (HWND)cvGetWindowHandle("Image (AviProcessor)");*/
			//	  ::SendMessage(hWnd,WM_PAINT,NULL,NULL); // force the window to repaint
			//}

			// increment frame number
			proc->incFrameNumber();

		

         } else {

			proc->stopIt();
              }
  // }
    _endthreadex( 0 );
	return 0;
}

	 // set the callback function that will be called for each frame
	  void setImageProcess(void (*p)(IplImage *)) {

		  process= p;
	  }

	  // stop the capture
	  void stopIt() {

          WaitForSingleObject(mut, INFINITE);
		  stop= true;
          ReleaseMutex(mut);
	  }

	  bool isStopped() {

		  bool c;

          WaitForSingleObject(mut, INFINITE);
		  c= stop;
          ReleaseMutex(mut);

		  return c;
	  }
   
	  // the getter and setters to determine if you want the 
	  // callback to be called
	  void callProcess() {

          WaitForSingleObject(mut, INFINITE);
		  call= true;
          ReleaseMutex(mut);
	  }

	  void dontCallProcess() {

          WaitForSingleObject(mut, INFINITE);
		  call= false;
          ReleaseMutex(mut);
	  }

	  bool callingProcess() {

		  bool c;

          WaitForSingleObject(mut, INFINITE);
		  c= call;
          ReleaseMutex(mut);

		  return c;
	  }

	  // the getter and setter to determine if you want to display 
	  // the processed frames
	  void setDisplay(bool b) {
	  
          WaitForSingleObject(mut, INFINITE);
		  if (b)
			cvNamedWindow("Image (AviProcessor)");
		  else
			cvDestroyWindow("Image (AviProcessor)");

		  display= b;
          ReleaseMutex(mut);
	  }

	  bool isDisplayed() {
	  
		  bool d;

          WaitForSingleObject(mut, INFINITE);
		  d= display;
          ReleaseMutex(mut);

		  return d;
	  }

	  // the getter and setter to introduce a delay between each frame
	  void setDelay(DWORD d) {
	  
		  WaitForSingleObject(mut, INFINITE);
		  delay= d;
		  ReleaseMutex(mut);
	  }

	  DWORD getDelay() {
	  
		  DWORD d;

          WaitForSingleObject(mut, INFINITE);
		  d= delay;
          ReleaseMutex(mut);

		  return d;
	  }

	  // a count is kept of the current frame number
	  long getFrameNumber() {
	  
		  long f;

		  WaitForSingleObject(mut, INFINITE);
		  f= fnumber;
		  ReleaseMutex(mut);

		  return f;
	  }

	  // size of the frames of the sequence
	  CvSize getFrameSize() {
	
		  if (img==0)
			  return cvSize(0,0);

		  return cvSize(img->width,img->height);
	  }

	 

	  // to grad (and process) only one frame
	  bool grabOneFrame() {

	    // make sure that a thread is not already running
	    if (!isStopped())
			return false;

		if(cvGrabFrame(capture)) {

			// gets the current image
			img=cvRetrieveFrame(capture);           

			// calling the process function
			if (callingProcess())
				process(img);

			if (display) {

				CvScalar sc;
				char text[50];
				sprintf(text,"Frame #%6d",fnumber);

				sc= cvGet2D(img,20,20);
				if (sc.val[1]>128) 
					cvPutText(img, text, cvPoint(20,20), &(font), cvScalar(0,0,0));
				else
					cvPutText(img, text, cvPoint(20,20), &(font), cvScalar(255,255,255));


				cvShowImage( "Image (AviProcessor)", img );
				HWND hWnd= (HWND)cvGetWindowHandle("Image (AviProcessor)");
				::SendMessage(hWnd,WM_PAINT,NULL,NULL); // force the window to repaint
			}

			// increments current frame number
			incFrameNumber();
		}

		return true;
	  }

	  // to grab (and process) the frame sequence
	  bool run() {

	    // make sure that a thread is not already running
	    if (!isStopped())
			return false;
	
		// destroy any previoulsy created thread
		if (hThread != 0)
		 CloseHandle( hThread );

	    stop= false;

      capture = cvCaptureFromCAM(0);

		// start the thread
		hThread = (HANDLE)_beginthreadex( NULL, 0, 
         processImages,reinterpret_cast<LPVOID>(this), 0, &threadID );

		return true;
	  }

	  // to restart with a new video
	  bool restart() {

	    // make sure that a thread is not already running
	    if (!isStopped())
			return false;

		// destroy any previoulsy created thread
		if (hThread != 0)
		 CloseHandle( hThread );

		fnumber=0;
		stop= false;
	   cvReleaseCapture(&capture);
		capture = cvCaptureFromCAM(0);

		return true;
	  }
};